import {
    _decorator,
    Component,
    Node,
    EventTouch,
    Vec3,
    UITransform,
    Vec2,
    Rect,
    tween,
    Tween,
    Vec3 as Vec3Tween
} from 'cc';
import {AutoSortArea} from "db://assets/app-bundle/app-view/page/game/native/expansion/AutoSortArea";
import {TileType} from "db://assets/app-bundle/app-view/page/game/native/PageGame";
import {app} from "db://assets/app/app";

const {ccclass, property} = _decorator;

@ccclass('Draggable')
export class Draggable extends Component {
    private _isDragging: boolean = false;
    private _startPos: Vec2 = new Vec2();     // 触摸起始点（UI 坐标）
    private _nodePos: Vec2 = new Vec2();       // 节点初始位置（世界坐标）

    @property(Node)
    targetArea: Node = null; // 目标区域节点，需在编辑器中指定

    @property({
        tooltip: '是否可拖动，默认为 true'
    })
    isDraggable: boolean = true; // 控制是否允许拖动
    onChangeTargetArea: () => void = null; // 可以在外部绑定一个回调函数
    onSortInArea: () => void = null; // 可以在外部绑定一个回调函数  排序

    start() {
        // 绑定触摸事件到当前节点
        this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
        this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); // 触摸取消也结束拖动
    }

    onDestroy() {
        // 移除事件监听，防止内存泄漏
        this.node.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.node.off(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.node.off(Node.EventType.TOUCH_END, this.onTouchEnd, this);
        this.node.off(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    /**
     * 触摸开始时记录初始位置并播放拖动开始动画（如果可拖动）
     */
    onTouchStart(event: EventTouch) {
        if (!this.isDraggable) return; // 如果不可拖动，直接返回
        app.manager.sound.playEffect({name: 'effect/clickMahjong'})

        this._isDragging = true;

        // 获取触摸点在屏幕上的 UI 坐标
        const touchPos = event.getUILocation();
        this._startPos.set(touchPos.x, touchPos.y);

        // 记录节点当前的世界坐标
        this._nodePos.set(this.node.position.x, this.node.position.y);

        // 播放拖动开始动画：放大 + 透明度降低
        this.playDragStartAnimation();
    }

    /**
     * 播放拖动开始动画
     */
    private playDragStartAnimation() {
        // 先停止之前的 Tween（避免叠加）
        tween(this.node).stop();

        // 放大到 1.1 倍
        tween(this.node)
            .to(0.2, {scale: new Vec3(1.1, 1.1, 1)})
            .start();
    }

    /**
     * 触摸移动时更新节点位置（如果可拖动）
     */
    onTouchMove(event: EventTouch) {
        if (!this.isDraggable || !this._isDragging) return; // 如果不可拖动或未处于拖动状态，直接返回

        const touchPos = event.getUILocation(); // 当前触摸点 UI 坐标

        // 计算偏移量（UI 坐标系）
        const delta = new Vec2(
            touchPos.x - this._startPos.x,
            touchPos.y - this._startPos.y
        );

        // 更新节点位置：初始位置 + 偏移量
        const newPos = new Vec2(this._nodePos.x + delta.x, this._nodePos.y + delta.y);
        this.node.setPosition(newPos.x, newPos.y, 0); // 使用 Vec3 设置位置（z=0）
    }

    /**
     * 触摸结束时判断是否在目标区域内并播放对应动画（如果可拖动）
     */
    onTouchEnd(event: EventTouch) {
        //触摸结束 回调给game当前节点的世界坐标 来改变当前可停放的目标区域
        this.onChangeTargetArea()
        if (!this.isDraggable || !this._isDragging) return; // 如果不可拖动或未处于拖动状态，直接返回
        this._isDragging = false;

        if (this.targetArea) {
            // 获取当前节点的世界坐标
            const nodeWorldPos = this.node.getWorldPosition();

            // 获取目标区域的包围盒（世界坐标系）
            const targetWorldBounds = this.getTargetWorldBounds();

            // 判断节点是否在目标区域内
            if (this.isPointInRect(nodeWorldPos, targetWorldBounds)) {
                // 获取目标区域的 AutoSortArea 组件
                const autoSortArea = this.targetArea.getComponent(AutoSortArea);
                if (!autoSortArea) {
                    console.warn('目标区域节点缺少 AutoSortArea 组件！');
                    return;
                }
                // 解析当前节点的麻将牌信息
                const currentTile = this.parseMahjongTileName(this.node.name);
                if (!currentTile) {
                    console.warn(`无法解析节点 ${this.node.name} 的麻将牌信息！`);
                    return;
                }
                //没有 子节点直接放  或者相等
                if (this.targetArea.children.length == 0 || this.isMahjongTileEqual(currentTile, autoSortArea.targetMahjongTile)) {
                    // 如果目标区域没有指定的麻将牌，直接设置当前麻将牌为目标麻将牌
                    autoSortArea.targetMahjongTile = currentTile;
                    // 在目标区域内，播放成功动画
                    this.playSuccessAnimation()
                    // 将节点移动到目标区域节点下
                    this.node.parent = this.targetArea;
                    app.manager.sound.playEffect({name: 'effect/moveSuccess'})
                    this.onSortInArea()
                    console.log('拖动成功，麻将牌放置在目标区域');
                } else {
                    this.playReturnAnimation();
                    console.log('麻将牌未放置在对应区域，返回原位置');
                }
            } else {
                // 不在目标区域，播放返回动画
                this.playReturnAnimation();
                console.log('麻将牌未放置在目标区域，返回原位置');
            }
        } else {
            console.warn('未设置目标区域（targetArea），无法判断是否放置成功');
            // 默认返回原位置并播放返回动画
            this.playReturnAnimation();
        }
    }

    /**
     * 解析麻将牌名称，返回麻将牌对象
     * @param name 麻将牌节点名称（如 "1W" 表示万1，"2T" 表示条2）
     * @returns 解析后的麻将牌对象，如果解析失败则返回 null
     */
    private parseMahjongTileName(name: string): { type: TileType; value: number } | null {
        if (!name || name.length < 2) {
            console.warn(`无效的麻将牌名称: ${name}`);
            return null;
        }

        const typeChar = name.charAt(0); // 第一个字符表示类型（如 "1"、"2"）
        const valueChar = name.charAt(1); // 第二个字符表示值（如 "W"、"T"）

        // 解析麻将牌值
        const type = Number(typeChar);
        const value = Number(valueChar);

        return {type, value};
    }

    /**
     * 比较两张麻将牌是否相等
     * @param tile1 麻将牌对象 1
     * @param tile2 麻将牌对象 2
     * @returns 如果两张麻将牌类型和值都相同，则返回 true，否则返回 false
     */
    private isMahjongTileEqual(tile1: { type: TileType; value: number }, tile2: {
        type: TileType;
        value: number
    }): boolean {
        return tile1.type === tile2.type && tile1.value === tile2.value;
    }

    /**
     * 播放成功动画：缩小 + 闪烁
     */
    private playSuccessAnimation() {
        // 先停止之前的 Tween
        tween(this.node).stop();

        // 缩小到 0.9 倍，然后恢复到 1 倍，同时闪烁（透明度变化）
        tween(this.node)
            .to(0.1, {scale: new Vec3(0.9, 0.9, 1)})
            .to(0.1, {scale: new Vec3(1, 1, 1)})
            .call(() => {
                console.log('拖动成功，放置在目标区域');
            })
            .start();
    }

    /**
     * 播放返回动画：弹性回弹到原位置
     */
    private playReturnAnimation() {
        // 先停止之前的 Tween
        tween(this.node).stop();

        // 先恢复透明度
        tween(this.node)
            .to(0.2, {position: new Vec3(this._nodePos.x, this._nodePos.y, 0), easing: 'elasticOut'})
            .to(0.1, {scale: new Vec3(1, 1, 1)})
            .call(() => {
                console.log('未放置在目标区域，返回原位置');
            })
            .start();
    }

    /**
     * 获取目标区域的包围盒（世界坐标系）
     */
    private getTargetWorldBounds(): Rect {
        if (!this.targetArea) return new Rect();

        const uiTransform = this.targetArea.getComponent(UITransform);
        if (!uiTransform) {
            console.warn('目标区域节点缺少 UITransform 组件');
            return new Rect();
        }

        // 获取目标区域在本地坐标系下的包围盒
        const localRect = new Rect(0, 0, uiTransform.width, uiTransform.height);

        // 将本地包围盒转换为世界坐标系下的包围盒
        const worldPos = this.targetArea.getWorldPosition();
        const anchorX = uiTransform.anchorX; // 锚点 x（0~1）
        const anchorY = uiTransform.anchorY; // 锚点 y（0~1）

        // 计算世界坐标系下的包围盒
        const left = worldPos.x - uiTransform.width * anchorX;
        const right = left + uiTransform.width;
        const bottom = worldPos.y - uiTransform.height * anchorY;
        const top = bottom + uiTransform.height;

        return new Rect(left, bottom, right - left, top - bottom);
    }

    /**
     * 判断一个点是否在矩形区域内
     */
    private isPointInRect(point: Vec3, rect: Rect): boolean {
        return (
            point.x >= rect.x &&
            point.x <= rect.x + rect.width &&
            point.y >= rect.y &&
            point.y <= rect.y + rect.height
        );
    }
}